//
// Created by denglibin on 2019/6/17 0017.
//
#include <vector>
#include <queue>
#include <memory>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <future>
#include <functional>
#include <stdexcept>

/**类模板std :: function是一个通用的多态函数包装器。 std :: function的实例可以存储，复制和调用任何可调用的目标 ：
 * 包括函数，lambda表达式，绑定表达式或其他函数对象，以及指向成员函数和指向数据成员的指针。
 * 当std::function对象未包裹任何实际的可调用元素，调用该std::function对象将抛出std::bad_function_call异
 *
 * result_of() 函数使用到了模板元编程技术，对于 F(Arg1, Arg2... Arg3) ，其中F 是一个可调用对象（函数指针，函数引用， 成员函数指针，或者是函数对象）f 的类型，
 * Arg是其参数arg1,  arg2 的类型。 那么reslut_of <Func(Arg1, Arg2... Arg3)> :: type 表示的也就是 f（arg1，arg2 ... argn）的返回值的类型

    线程池：参考github项目 https://github.com/progschj/ThreadPool
*/
class ThreadPool{
public:
    ThreadPool(size_t);
    //添加任务到线程池
    template <class F,class... Args> //函数和参数
    auto enqueue(F&& f, Args&&... args)->std::future<typename std::result_of<F(Args...)>::type>;
    ~ThreadPool();
private:
    //线程执行的任务队列
    std::queue<std::function<void()>> tasks;
    //工作线程
    std::vector<std::shared_ptr<std::thread>> workers;
    //std::vector<std::thread> workers;
    //互斥锁
    std::mutex queue_mutex;
    //条件变量
    std::condition_variable condition;
    volatile bool stop;
};

//构造函数
inline ThreadPool::ThreadPool(size_t threads) : stop(false) {
   //创建工作线程
    for(size_t i = 0; i < threads; i++){
        //创建线程对象放入到集合中
        std::shared_ptr<std::thread> p = std::make_shared<std::thread>([this]()->void{
            //线程执行的代码
            while (true){
                std::function<void()> task; //函数
                std::unique_lock<std::mutex> lock(this->queue_mutex, std::defer_lock);//默认不加锁
                //加锁（从队列公共资源中取任务需要加锁）
                lock.lock();
                //从队列里面取任务(没有任务任务则阻塞)
                this->condition.wait(lock, [this]{ //条件阻塞 条件为false时调用该wait函数才会阻塞当前线程 (条件为true，不会阻塞)
                    //return this->stop || !this->tasks.empty(); //两个都为false才返回false
                    return !(!this->stop && this->tasks.empty()); //没关闭并且任务为空则阻塞 （阻塞会释放锁）
                });
                //线程池关闭了并且没有任务了 结束线程代码
                if(this->stop && tasks.empty()){
                    return;
                }
                //有任务(不管是否stop) move:左值引用转换为右值引用
                printf("%d get a task\n", std::this_thread::get_id());
                task = std::move(this->tasks.front());
                this->tasks.pop();
                lock.unlock();
                task(); //执行任务
            }
        });
        workers.emplace_back(p);
       /*
        workers.emplace_back([this]{
            //线程执行的代码
            while (true){
                std::function<void()> task; //函数
                std::unique_lock<std::mutex> lock(this->queue_mutex, std::defer_lock);//默认不加锁
                //加锁（从队列公共资源中取任务需要加锁）
                lock.lock();
               //从队列里面取任务(没有任务任务则阻塞)
               this->condition.wait(lock, [this]{ //条件阻塞 条件为false时调用该wait函数才会阻塞当前线程 (条件为true，不会阻塞)
                   //return this->stop || !this->tasks.empty(); //两个都为false才返回false
                   return !(!this->stop && this->tasks.empty()); //没关闭并且任务为空则阻塞 （阻塞会释放锁）
               });
               //线程池关闭了并且没有任务了 结束线程代码
               if(this->stop && tasks.empty()){
                   return;
               }
               //有任务(不管是否stop) move:左值引用转换为右值引用
               printf("%d get a task\n", std::this_thread::get_id());
               task = std::move(this->tasks.front());
               this->tasks.pop();
               lock.unlock();
               task(); //执行任务
            }
        });*/
    }
}

//添加任务到线程池
template<class F, class... Args>
auto ThreadPool::enqueue(F &&f, Args &&... args)-> std::future<typename std::result_of<F(Args...)>::type> {
    //定义任务返回类型别名      std::result_of 在编译的时候推导出一个函数表达式的返回值类型
    using return_type = typename std::result_of<F(Args...)>::type;
    //定义package_task类型别名
    using packaged_task_type = std::packaged_task<return_type(Args&&...)>;
    //用make_shared(智能指针)创建一个package_task对象返回指针
    auto task = std::make_shared<packaged_task_type >(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
            );

    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);

        // 关闭线程池后不允许加入任务
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");
        //(lambda表达式函数)加入到队列中
        tasks.emplace([task](){
            printf("execute task\n");
            (*task)(); //再调用函数
        });
    }
    //唤醒一个工作线程
    condition.notify_one();
    return res; //返回异步执行结果
}
inline ThreadPool::~ThreadPool() {
    printf("destruct ThreadPool:\n");
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
    condition.notify_all();
    for(std::shared_ptr<std::thread>  worker: workers)
        worker->join(); //阻塞主线程等待子线程完成

}

int main48(){
    ThreadPool pool(2);
    std::vector< std::future<int> > results;
    for(int i = 0; i < 10; i++){
       results.emplace_back(pool.enqueue([i]()-> int {
            printf("%d, hello thread pool,%d\n",std::this_thread::get_id(),i);
            return i;
        }));
    }
    std::this_thread::sleep_for(std::chrono::seconds(2));
    for(auto && result: results)
        printf("%d ",result.get());
    return 0;
}